#include "srpc/srpc.h"
#include <stdio.h>
#include <fcntl.h>
#include <mutex>
#include <condition_variable>
#include <thread>

#define OK(x) (x) == 0
#define ERR(x) (x) != 0

const std::string ADDRESS = "127.0.0.1:5000";

std::mutex mtx;
std::condition_variable syncCV;
bool ready;

void handle_echo(EchoResponsePB *response)
{
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << "Echo:" << response->message() << std::endl;
    ready = true;
    syncCV.notify_one();
}


void cmd_msg_cb(BUFEVENT bev , void *_channel)
{
    srpc::TcpRpcChannel *channel = (srpc::TcpRpcChannel*)_channel;
    EchoService_Stub stub(channel);
    struct evbuffer *input = bufferevent_get_input(bev);
    int size = sizeof(input);
    char line[1024];
    memset(line, 0, sizeof(line));
    bufferevent_read(bev, line, sizeof(input));
    evbuffer_drain(input,evbuffer_get_length(input));
    line[size + 1] = '\0';
    EchoRequestPB request =  EchoRequestPB();
    EchoResponsePB* response = new EchoResponsePB();
    request.set_message(std::string(line));
    auto doneClosure = google::protobuf::NewCallback(&handle_echo, response);
    // stub.Echo(NULL, &request, response, doneClosure);
    struct para 
    {
        RpcController ctrl;
        EchoRequestPB request;
        EchoResponsePB* response;

    }
    std::thread th(&EchoService_Stub::Echo, &stub, NULL, &request, response, doneClosure);
    // TODO add sync 
    ready = false;
    {
        std::unique_lock<std::mutex> lock(mtx);
        while (!ready) syncCV.wait(lock);
    }
    std::cout << "Echo sync:" << response->message() << std::endl;
    delete response;
}

int main()
{
    int status = 0;
    EV_BASE ev_base = event_init();
    if (ev_base == nullptr)
    {
        std::cout << "init ev_base faild" << std::endl;
        return 1;
    }

    srpc::TcpRpcChannelClient channelClient(ev_base);
    status = channelClient.Start();
    if (ERR(status))
    {
        std::cout << "start client failed" << std::endl;
        return 1;
    }

    srpc::TcpRpcChannel channel;
    status = channelClient.OpenChannel(ADDRESS, &channel);
    if (ERR(status))
    {
        std::cout << "open channel failed" << std::endl;
        return 1;
    }

    status = evutil_make_socket_nonblocking(STDIN_FILENO);
    if(ERR(status))
    {
        std::cout << "make stdin nonblocing failed" << std::endl;
    }

    BUFEVENT pBufferEvent = bufferevent_socket_new(ev_base, STDIN_FILENO, BEV_OPT_CLOSE_ON_FREE);
    if (pBufferEvent == nullptr)
    {
        std::cout << "init pBufferEvent event failed" << std::endl;
        return 1;
    }

    bufferevent_setcb(pBufferEvent, cmd_msg_cb, NULL, NULL, (void*) &channel);
    bufferevent_enable(pBufferEvent, EV_READ | EV_PERSIST);
    event_dispatch();
}
